﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Threading.Tasks;


namespace Dwarf.Managed
{
    /// <summary>
    /// Solve the Dense Linear Algebra problem with TPL parallelization  
    /// </summary>
    /// <remarks> 
    /// For the demonstration purposes of the project, 
    /// we have chosen matrix-matrix multiplication.
    /// </remarks>
    class Solver
    {
        /// <summary>
        /// Short dwarf's description.
        /// </summary>
        public const String DWARF_NAME = "Dense Linear Algebra, managed tpl kernel";

        /// <summary>
        /// The size of square matrix.
        /// </summary>
        public int msize;

        /// <summary>
        /// Left matrix.
        /// </summary>
        public double[][] lgrid;

        /// <summary>
        /// Right matrix.
        /// </summary>
        public double[][] rgrid;

        /// <summary>
        /// Result matrix.
        /// </summary>
        public double[][] result;

        /// <summary>
        /// Empty constructor.
        /// </summary>
        public Solver()
        {
        }

        /// <summary>
        /// Transpose a square matrix
        /// <param name=matrix>
        /// The ragged square array to transpose.  Returns a new ragged transposed array
        /// </param>
        /// <param name=n>
        /// Dimension of the square array
        /// </param>
        /// <typeparam name=T>
        /// Element type of the array
        /// </typeparam>
        /// </summary>
        static private T[][] Transpose<T>(T[][] matrix, int n)
        {
            T[][] transpose = new T[n][];
            for (int i = 0; i < n; i++)
            {
                transpose[i] = new T[n];
            }
            for (int i = 0; i < n; i++)
            {
                transpose[i][i] = matrix[i][i];
                for (int j = i + 1; j < n; j++)
                {
                    transpose[i][j] = matrix[j][i];
                    transpose[j][i] = matrix[i][j];
                }
            }
            return transpose;
        }

        /// <summary>
        /// TPL based method for matrix-matrix multiplication.
        /// </summary>
        public void Solve()
        {
            // transpose the right hand array to improve memory accessing
            double[][] rtransposed = Transpose<Double>(rgrid, msize);

            //Multiple lgrid by rgrid (transposed)
            //    Note that row and cell are factored out
            //    since the optimizers can't hoist all of the bounds checking
            //    and the common addressing subexpressions without doing it. 
            //    (Strickly speaking it would be incorrect to do so.)
            Parallel.For(0, msize, i =>                     // TPL directive Loop for number of rows in left matrix 
            {
                double[] row = result[i];
                double[] leftRow = lgrid[i];
                for (int j = 0; j < msize; j++)             // Loop for number of columns in right matrix.
                {
                    double cell = 0.0;
                    double[] rightColumn = rtransposed[j];
                    for (int k = 0; k < msize; k++)         // Loop for number of columns in left matrix and rows in right matrix.
                    {
                        cell += leftRow[k] * rightColumn[k];
                    }
                    row[j] = cell;
                }
            });
        }
    }
}
